package com.lokiy.adapter

import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding

/**
 * LoadMoreAdapter
 * @author Lokiy
 * @date 2023/4/6 13:30
 */

/**
 * ViewBindingAdapter
 *
 *    val adapter = ViewBindingAdapter<String, FragmentItem1Binding> { holder, item ->
 *        holder.binding.text.text = item
 *    }
 *
 * see more [MixViewBindingAdapter]
 */
inline fun <T, reified Binding : ViewBinding> ViewBindingAdapter(
    pageSize: Int = 20,
    noinline onLoadMore: ViewBindingAdapter<T, Binding, BindingHolder<Binding>>.() -> Unit = {},
    noinline onViewRecycled: (BindingHolder<Binding>) -> Unit = {},
    noinline onBindHolder: ViewBindingAdapter<T, Binding, BindingHolder<Binding>>.(BindingHolder<Binding>, T) -> Unit,
) = object : LoadMoreAdapter<T, Binding>(pageSize = pageSize, onLoadMore = onLoadMore, onViewRecycled = onViewRecycled, onBindHolder = onBindHolder){}


/**
 * MixViewBindingAdapter
 *
 *     class A(override val binding: Class<out ViewBinding> = ItemFooterBinding::class.java) : BindingItem
 *
 *     class B(val text: String, override val binding: Class<out ViewBinding> = FragmentItem1Binding::class.java) : BindingItem
 *
 *     class C(val text: String, val text1: String, override val binding: Class<out ViewBinding> = FragmentItem2Binding::class.java) : BindingItem
 *
 * ...
 *
 *     val adapter = MixViewBindingAdapter<BindingItem> { holder, item ->
 *         val binding = holder.binding
 *         when (item) {
 *              is A -> {
 *                  binding as ItemFooterBinding
 *                  binding.loadingTips.text = holder.adapterPosition.toString()
 *              }
 *              is B -> {
 *                  binding as FragmentItem1Binding
 *                  binding.text.text = item.text
 *              }
 *              is C -> {
 *                  binding as FragmentItem2Binding
 *                  binding.text.text = item.text
 *                  binding.text1.text = item.text1
 *              }
 *         }
 *     }
 *
 * or
 *
 *     val adapter = MixViewBindingAdapter<Any>(onGetBindingType = {
 *         if (it is A) {
 *             ItemFooterBinding::class.java
 *         } else if (it is B) {
 *             FragmentItem1Binding::class.java
 *         } else if (it is C) {
 *             FragmentItem2Binding::class.java
 *         } else {
 *             null
 *         }
 *     }) { holder, item ->
 *         val binding = holder.binding
 *         when (item) {
 *             is A -> {
 *                 binding as ItemFooterBinding
 *                 binding.loadingTips.text = holder.adapterPosition.toString()
 *             }
 *
 *             is B -> {
 *                 binding as FragmentItem1Binding
 *                 binding.text.text = item.text
 *             }
 *
 *             is C -> {
 *                 binding as FragmentItem2Binding
 *                 binding.text.text = item.text
 *                 binding.text1.text = item.text1
 *             }
 *
 *         }
 *     }
 *
 *
 * @param onLoadMore load next page
 * @param pageSize page size.
 * @param onViewRecycled see [RecyclerView.Adapter.onViewRecycled]
 * @param onBindHolder bind data
 *
 */
fun <T> MixViewBindingAdapter(
    pageSize: Int = 20,
    onGetBindingType: (T) -> Class<out ViewBinding>? = { null },
    onLoadMore: MixViewBindingAdapter<T>.() -> Unit = {},
    onViewRecycled: (MixBindingHolder) -> Unit = {},
    onBindHolder: MixViewBindingAdapter<T>.(MixBindingHolder, T) -> Unit,
) = LoadMoreAdapter(pageSize = pageSize, onGetBindingType = onGetBindingType, onLoadMore = onLoadMore, onViewRecycled = onViewRecycled, onBindHolder = onBindHolder)

/**
 * MixViewBindingAdapter
 */
typealias MixViewBindingAdapter<T> = ViewBindingAdapter<T, ViewBinding, MixBindingHolder>

/**
 * MixBindingHolder
 */
typealias MixBindingHolder = BindingHolder<ViewBinding>


open class LoadMoreAdapter<T, Binding : ViewBinding>(
    private val pageSize: Int = 20,
    private val onGetBindingType: (T) -> Class<out ViewBinding>? = { null },
    private val onLoadMore: ViewBindingAdapter<T, Binding, BindingHolder<Binding>>.() -> Unit = {},
    private val onViewRecycled: (BindingHolder<Binding>) -> Unit = {},
    private val onBindHolder: ViewBindingAdapter<T, Binding, BindingHolder<Binding>>.(BindingHolder<Binding>, T) -> Unit,
) : ViewBindingAdapter<T, Binding, BindingHolder<Binding>>() {
    private var isOnLoadMore = false
    private var isLoadEnd = false

    override fun onGetBindingType(item: T): Class<out ViewBinding>? = onGetBindingType.invoke(item) ?: run {
        if (item is BindingItem) {
            item.binding
        } else {
            null
        }
    }

    override fun onBindViewHolder(holder: BindingHolder<Binding>, item: T?) {
        item ?: return
        onBindHolder.invoke(this, holder, item)
    }

    override fun getItemViewType(position: Int): Int {
        if (position >= itemCount - 2 && isOnLoadMore.not() && isLoadEnd.not()) {
            isOnLoadMore = true
            onLoadMore.invoke(this)
        }
        return super.getItemViewType(position)
    }

    override fun onViewRecycled(holder: BindingHolder<Binding>) {
        super.onViewRecycled(holder)
        onViewRecycled.invoke(holder)
    }

    override fun addAll(list: Collection<T>?) {
        isOnLoadMore = false
        isLoadEnd = (list?.size ?: 0) < pageSize
        super.addAll(list)
    }

    override fun refresh(list: Collection<T>?) {
        isOnLoadMore = false
        isLoadEnd = (list?.size ?: 0) < pageSize
        super.refresh(list)
    }

    override fun notifyChanged(list: Collection<T>?) {
        isOnLoadMore = false
        isLoadEnd = (list?.size ?: 0) < pageSize
        super.notifyChanged(list)
    }
}
